
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

using LongNet.Util;

namespace LongNet.Config
{
    /// <summary>
    /// An thumbnail image helper to generate or save thumbnail images.
    /// This class cannot be inherited.
    /// </summary>
    public static class ThumbnailImage
    {
        private static LongNet.Util.LogManager.Logger logger = LongNet.Util.LogManager.GetCurrentClassLogger();

        #region ThumbnailMode

        /// <summary>
        /// The thumbnail modes used in <see cref="ThumbnailImage"/>. The Cut and Auto mode are recommended.
        /// </summary>
        public enum ThumbnailMode : byte
        {
            /// <summary>
            /// Specify the width and height in proportion to.
            /// </summary>
            Width,

            /// <summary>
            /// Specify the height and width in proportion to.
            /// </summary>
            Height,

            /// <summary>
            /// Specify the width and height scaling (possibly deformed).
            /// </summary>
            Scaling,

            /// <summary>
            /// Specify the height and width of the cut (not deformed, it is recommended to use this mode).
            /// </summary>
            Cut,

            /// <summary>
            /// According to width and height to select Width or Height mode automatically.
            /// </summary>
            Auto
        }

        #endregion

        #region SaveThumbnailImage/GenerateThumbnailImage

        /// <summary>
        /// Saves a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImagePath">The path of the original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(string originalImagePath, string savePath, int maxWidth, int maxHeight)
        {
            return SaveThumbnailImage(originalImagePath, savePath, maxWidth, maxHeight, ThumbnailMode.Cut, 100);
        }

        /// <summary>
        /// Saves a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImageStream">The <see cref="Stream"/> of the original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(Stream originalImageStream, string savePath, int maxWidth, int maxHeight)
        {
            return SaveThumbnailImage(originalImageStream, savePath, maxWidth, maxHeight, ThumbnailMode.Cut, 100);
        }
        
        /// <summary>
        /// Saves a thumbnail by the <c>ThumbnailMode.Cut</c> mode.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(Image originalImage, string savePath, int maxWidth, int maxHeight)
        {
            return SaveThumbnailImage(originalImage, savePath, maxWidth, maxHeight, ThumbnailMode.Cut, 100);
        }

        /// <summary>
        /// Saves a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImagePath">The path of the original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <param name="mode">The <see cref="ThumbnailMode"/> to make thumbnail by.</param>
        /// <param name="quality">The quality must in range [1, 100].</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(string originalImagePath, string savePath, int maxWidth, int maxHeight, ThumbnailMode mode, int quality)
        {
            if (originalImagePath.IsNullOrTrimedEmpty())
                throw new ArgumentException("The originalImagePath is null or empty.", "originalImagePath");
            if (savePath.IsNullOrTrimedEmpty())
                throw new ArgumentException("The savePath is null or empty.", "savePath");

            Image thumbnail = null;
            Image originalImage = null;

            try
            {
                originalImage = Image.FromFile(originalImagePath);
                thumbnail = GenerateThumbnailImage(originalImage, maxWidth, maxHeight, mode);

                var ici = ImageUtils.GetImageCodecInfo(savePath);
                if (ici == null)
                    thumbnail.Save(savePath, ImageUtils.GetImageFormat(savePath));
                else
                    thumbnail.Save(savePath, ici, ImageUtils.GetQualityEncoderParameters(quality));

                return true;
            }
            catch (Exception)
            {
               logger.Error("Fail to save thumbnail image : {0}", savePath);
                return false;
            }
            finally
            {
                if (thumbnail != null)
                    thumbnail.Dispose();
                if (originalImage != null)
                    originalImage.Dispose();
            }
        }

        /// <summary>
        /// Saves a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImageStream">The <see cref="Stream"/> of the original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <param name="mode">The <see cref="ThumbnailMode"/> to make thumbnail by.</param>
        /// <param name="quality">The quality must in range [1, 100].</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(Stream originalImageStream, string savePath, int maxWidth, int maxHeight, ThumbnailMode mode, int quality)
        {
            if (originalImageStream == null)
                throw new ArgumentNullException("originalImageStream", "The originalImageStream is null.");
            if (savePath.IsNullOrTrimedEmpty())
                throw new ArgumentException("The savePath is null or empty.", "savePath");

            Image thumbnail = null;

            try
            {
                thumbnail = GenerateThumbnailImage(originalImageStream, maxWidth, maxHeight, mode);

                var ici = ImageUtils.GetImageCodecInfo(savePath);
                if (ici == null)
                    thumbnail.Save(savePath, ImageUtils.GetImageFormat(savePath));
                else
                    thumbnail.Save(savePath, ici, ImageUtils.GetQualityEncoderParameters(quality));

                return true;
            }
            catch (Exception ex)
            {
                if (logger.IsErrorEnabled)
                    logger.Error(ex, "Fail to save thumbnail image : {0}", savePath);

                return false;
            }
            finally
            {
                if (thumbnail != null)
                    thumbnail.Dispose();
            }
        }

        /// <summary>
        /// Saves a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="savePath">The path to save the thumbnail made from original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <param name="mode">The <see cref="ThumbnailMode"/> to make thumbnail by.</param>
        /// <param name="quality">The quality must in range [1, 100].</param>
        /// <returns>True if the save was succesful.</returns>
        public static bool SaveThumbnailImage(Image originalImage, string savePath, int maxWidth, int maxHeight, ThumbnailMode mode, int quality)
        {
            if (originalImage == null)
                throw new ArgumentNullException("originalImage", "The originalImage is null.");
            if (savePath.IsNullOrTrimedEmpty())
                throw new ArgumentException("The savePath is null or empty.", "savePath");

            Image thumbnail = null;

            try
            {
                thumbnail = GenerateThumbnailImage(originalImage, maxWidth, maxHeight, mode);

                var ici = ImageUtils.GetImageCodecInfo(savePath);
                if (ici == null)
                    thumbnail.Save(savePath, ImageUtils.GetImageFormat(savePath));
                else
                    thumbnail.Save(savePath, ici, ImageUtils.GetQualityEncoderParameters(quality));

                return true;
            }
            catch (Exception ex)
            {
                if (logger.IsErrorEnabled)
                    logger.Error(ex, "Fail to save thumbnail image : {0}", savePath);

                return false;
            }
            finally
            {
                if (thumbnail != null)
                    thumbnail.Dispose();
            }
        }


        /// <summary>
        /// Makes a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImageStream">The <see cref="Stream"/> of the original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <param name="mode">The <see cref="ThumbnailMode"/> to make thumbnail by.</param>
        /// <returns>The thumbnail image.</returns>
        public static Image GenerateThumbnailImage(Stream originalImageStream, int maxWidth, int maxHeight, ThumbnailMode mode)
        {
            if (originalImageStream == null)
                throw new ArgumentNullException("originalImageStream", "The originalImageStream is null.");

            Image thumbnail = null;
            using (Image image = Image.FromStream(originalImageStream))
            {
                thumbnail = GenerateThumbnailImage(image, maxWidth, maxHeight, mode);
                image.Dispose();

                return thumbnail;
            }
        }

        /// <summary>
        /// Makes a thumbnail by the specified <see cref="ThumbnailMode"/>.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="maxWidth">The max width of the thumbnail.</param>
        /// <param name="maxHeight">The max height of the thumbnail.</param>
        /// <param name="mode">The <see cref="ThumbnailMode"/> to make thumbnail by.</param>
        /// <returns>The thumbnail image.</returns>
        public static Image GenerateThumbnailImage(Image originalImage, int maxWidth, int maxHeight, ThumbnailMode mode)
        {
            if (originalImage == null)
                throw new ArgumentNullException("originalImage", "The originalImage is null.");

            int x = 0;
            int y = 0;
            int toW = maxWidth;
            int toH = maxHeight;
            Image thumbnail = null;

            decimal w = (decimal)originalImage.Width;
            decimal h = (decimal)originalImage.Height;

            // if the size is smaller the the max, just return the cloned image
            if (w <= maxWidth && h <= maxHeight)
            {
                return originalImage.Clone<Image>();
            }

            using (var image = originalImage.Clone<Image>())
            {
                #region set size and position

                if (w > maxWidth || h > maxHeight)
                {
                    switch (mode)
                    {
                        case ThumbnailMode.Scaling:
                            break;
                        case ThumbnailMode.Width:
                            toH = (int)(Math.Round(h * maxWidth / w));
                            break;
                        case ThumbnailMode.Height:
                            toW = (int)(Math.Round(w * maxHeight / h));
                            break;
                        case ThumbnailMode.Auto:
                            if (w * maxHeight > h * maxWidth)
                            {
                                toW = maxWidth;
                                toH = (int)Math.Round(h * maxWidth / w);
                            }
                            else
                            {
                                toW = (int)Math.Round(w * maxHeight / h);
                                toH = maxHeight;
                            }
                            break;
                        case ThumbnailMode.Cut:
                            if (w * maxHeight > h * maxWidth)
                            {
                                y = 0;
                                w = h * maxWidth / maxHeight;
                                x = (int)Math.Round((image.Width - w) / 2);
                            }
                            else
                            {
                                x = 0;
                                h = w * maxHeight / maxWidth;
                                y = (int)Math.Round((image.Height - h) / 2);
                            }
                            break;
                        default:
                            throw new NotSupportedException("Not supported ThumbnailMode.");
                    }
                }

                #endregion

                thumbnail = new Bitmap(toW, toH);

                using (var graphics = Graphics.FromImage(thumbnail))
                {
                    graphics.InterpolationMode = InterpolationMode.High;
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.Clear(Color.Transparent);
                    // 在指定位置并且按指定大小绘制原图片的指定部分
                    var destRect = new Rectangle(0, 0, toW, toH);
                    var srcRect = new Rectangle(x, y, (int)w, (int)h);
                    graphics.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
                    graphics.Dispose();
                    image.Dispose();

                    return thumbnail;
                }
            }
        }

        #endregion
    }
}
